package com.carrot.uaa.authorization.sms;

import com.carrot.uaa.common.exception.InvalidCaptchaException;
import com.carrot.uaa.constant.SecurityConstants;
import com.carrot.uaa.domain.IAuthBusinessRuleService;
import com.carrot.uaa.enums.LoginTypeEnum;
import com.carrot.uaa.infrastructure.redis.RedisOperator;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Objects;


/**
 * 自定义短信验证码校验
 */
@Slf4j
@Component
public class SmsCaptchaLoginAuthenticationProvider extends DaoAuthenticationProvider {

    private final RedisOperator<String> redisOperator;

    @Resource
    private IAuthBusinessRuleService iAuthBusinessRuleService;
    /**
     * 通过构造方法调用父类的set方法设置UserDetailsService和PasswordEncoder
     * 注入RedisOperator
     * @param userDetailsService
     * @param passwordEncoder
     */
    public SmsCaptchaLoginAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder, RedisOperator<String> redisOperator){
        super.setPasswordEncoder(passwordEncoder);
        super.setUserDetailsService(userDetailsService);
        super.setHideUserNotFoundExceptions(false);
        this.redisOperator=redisOperator;
    }


    /**
     * 重写登录校验方法
     * @param userDetails as retrieved from the
     * {@link #retrieveUser(String, UsernamePasswordAuthenticationToken)} or
     * <code>UserCache</code>
     * @param authentication the current request that needs to be authenticated
     * @throws AuthenticationException
     */
    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        if (authentication.getCredentials() == null) {
            this.logger.debug("Failed to authenticate since no credentials provided");
            throw new BadCredentialsException(this.messages
                    .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        }

        //获取当前request
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if(requestAttributes==null){
            throw new InvalidCaptchaException("Failed to get the current request.");
        }
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();

        String account = String.valueOf(authentication.getPrincipal());
        //获取当前登录方式
        String loginType=request.getParameter(SecurityConstants.LOGIN_TYPE_NAME);
        String appName = request.getParameter(SecurityConstants.APP_NAME);
        if(Objects.equals(loginType, SecurityConstants.SMS_LOGIN_TYPE)
                || String.valueOf(LoginTypeEnum.SMS_CAPTCHA.getCode()).equals(loginType)){
            //手机号登录
            iAuthBusinessRuleService
                    .checkSmsCaptcha(account,String.valueOf(authentication.getCredentials()),appName);
        }else if(String.valueOf(LoginTypeEnum.PASSWD.getCode()).equals(loginType) ) {
            //密码登录
            String presentedPassword = authentication.getCredentials().toString();
            iAuthBusinessRuleService
                    .checkPassword(super.getPasswordEncoder(),account, userDetails.getPassword(),presentedPassword);
        }else {
            super.additionalAuthenticationChecks(userDetails, authentication);
        }
    }
}
